// J2ME GPS Track
// Copyright (C) 2006 Dana Peters
// http://www.qcontinuum.org/gpstrack

package uns.fit.gis.gps;

import javax.microedition.lcdui.*;
import javax.microedition.rms.*;
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import henson.midp.Float;
import java.util.*;
import org.qcontinuum.astro.*;

public class Track implements Runnable {
    
    final static private int DEGREESTOINT = 46603;
    
    final static public int FORMAT_NONE = 0;
    final static public int FORMAT_BINARY = 1;
    final static public int FORMAT_KML = 2;
    final static public int FORMAT_GPX = 3;
    final static public int FORMAT_CSV = 4;

    final static public int DESTINATION_EMAIL = 1;
    final static public int DESTINATION_BLUETOOTH = 2;
    final static public int DESTINATION_FILE = 3;
    
    private final static String mFormatExtension[] = { "", ".bin", ".kml", ".gpx", ".csv" };
    private final static String mFormatName[] = { "", "bin", "kml", "gpx", "csv" };

    private RecordStore mRecordStore;
    private String mTrackName;
    private boolean mStopThread;
    private Float mMinDistance, mDistance;
    private long mMinTime;
    private int mNoFixTime, mRunTime, mStopTime;

    public Track() {
        mRecordStore = null;
        mTrackName = "";
    }
    
    public void open() throws RecordStoreException {
        Preferences preferences = MobileGPS.getPreferences();
        mMinDistance = new Float(preferences.getMinDistance());
        mDistance = new Float(0);
        mMinTime = preferences.getMinTime() * 1000;
        mNoFixTime = mRunTime = mStopTime = 0;
        mRecordStore = null;
        try {
            mTrackName = MobileGPS.getDateString(System.currentTimeMillis());
            System.out.println("-------------mTrackName : "+mTrackName);
            mRecordStore = RecordStore.openRecordStore(mTrackName, true);
            mStopThread = false;
            new Thread(this).start();
        } catch (RecordStoreException ex) {
            if (mRecordStore != null)
                mRecordStore.closeRecordStore();
            throw ex;
        }
    }

    public void close() {
        mStopThread = true;
        if (mRecordStore != null) {
            try {
                mRecordStore.closeRecordStore();
            }
            catch (Exception ex) { }
            mRecordStore = null;
        }
    }

    public boolean isOpen() {
        return mRecordStore != null;
    }

    public int getNumRecords() {
        int numRecords = 0;
        if (mRecordStore != null) {
            try {
                numRecords = mRecordStore.getNumRecords();
            }
            catch (Exception ex) { }
        }
        return numRecords;
    }

    public String getName() {
        return mTrackName;
    }
    
    public Float getDistance() {
        return mDistance;
    }
    
    public int getNoFixTime() {
        return mNoFixTime;
    }
    
    public int getStopTime() {
        return mStopTime;
    }
    
    public int getRunTime() {
        return mRunTime;
    }

    public void run() {
        Thread thread = Thread.currentThread();
        EarthPosition earthPosition, lastEarthPosition = null, lastPointEarthPosition = null;
        long currentTime, deltaTime, lastTime = 0, lastPointTime = 0;
        Float distance;
        Gps gps = MobileGPS.getGps();
        try {
            while (!mStopThread) {
                thread.sleep(2000);
                System.out.println("WRITE A RECORD STORE GPS....!");
                currentTime = System.currentTimeMillis();
                if (lastTime == 0)
                    lastTime = currentTime;
                deltaTime = currentTime - lastTime;
                lastTime = currentTime;
                if (!gps.getFix()) {
                    mNoFixTime += deltaTime;
                } else {
                    if (gps.getSpeed().Less(3))
                        mStopTime += deltaTime;
                    else
                        mRunTime += deltaTime;
                    earthPosition = gps.getEarthPosition();
                    if (earthPosition != null) {
                        if (lastEarthPosition == null)
                            lastEarthPosition = earthPosition;
                        distance = lastEarthPosition.getDistance(earthPosition);
                        lastEarthPosition = earthPosition;
                        if (distance.Less(100))
                            mDistance = mDistance.Add(distance);
                        if (currentTime - lastPointTime >= mMinTime) {
                            if (lastPointEarthPosition == null)
                                lastPointEarthPosition = earthPosition;
                            distance = lastPointEarthPosition.getDistance(earthPosition);
                            if (distance.Less(10000) && distance.Great(mMinDistance)) {
                                writeRecord(earthPosition);
                                lastPointTime = currentTime;
                                lastPointEarthPosition = earthPosition;
                            }
                        }
                    }
                }
            }
        } catch (InterruptedException e) { }
    }

    private void writeRecord(EarthPosition earthPosition) {
        try {
            byte[] bytes = new byte[6];
            int latitude = (int)earthPosition.getLatitude().Mul(DEGREESTOINT).toLong();
            bytes[0] = (byte)(0xff & (latitude >> 16));
            bytes[1] = (byte)(0xff & (latitude >> 8));
            bytes[2] = (byte)(0xff & latitude);
            int longitude = (int)earthPosition.getLongitude().Mul(DEGREESTOINT).toLong();
            bytes[3] = (byte)(0xff & (longitude >> 16));
            bytes[4] = (byte)(0xff & (longitude >> 8));
            bytes[5] = (byte)(0xff & longitude);
            if (mRecordStore != null)
                mRecordStore.addRecord(bytes, 0, bytes.length);
        }
        catch (RecordStoreException e) { }
    }

    public static String getFormatExtension(int format) {
        return mFormatExtension[format];
    }

    public static String getFormatName(int format) {
        return mFormatName[format];
    }

    public static void write(int format, DataOutputStream out, String trackName, Gauge gauge) throws IOException, RecordStoreException {
        switch (format) {
            case FORMAT_BINARY:
                writeBinary(out, trackName, gauge);
                break;
            case FORMAT_KML:
                writeKml(out, trackName, gauge);
                break;
            case FORMAT_GPX:
                writeGpx(out, trackName, gauge);
                break;
            case FORMAT_CSV:
                writeCsv(out, trackName, gauge);
                break;
        }
    }
    
    private static void writeKml(DataOutputStream out, String trackName, Gauge gauge) throws IOException, RecordStoreException {
        RecordStore recordStore = null;
        try { 
            out.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n".getBytes());
            out.write("<kml xmlns=\"http://www.google.com/earth/kml/2\">\r\n".getBytes());
            out.write("  <Document>\r\n".getBytes());
            out.write("    <name>2006-11-01b</name>\r\n".getBytes());
            out.write("    <Style id=\"waypoint\">\r\n".getBytes());
            out.write("      <IconStyle>\r\n".getBytes());
            out.write("        <Icon>\r\n".getBytes());
            out.write("          <href>root://icons/bitmap-4.png</href>\r\n".getBytes());
            out.write("          <x>160</x>\r\n".getBytes());
            out.write("          <w>32</w>\r\n".getBytes());
            out.write("          <h>32</h>\r\n".getBytes());
            out.write("        </Icon>\r\n".getBytes());
            out.write("      </IconStyle>\r\n".getBytes());
            out.write("    </Style>\r\n".getBytes());
            out.write("    <Style id=\"track\">\r\n".getBytes());
            out.write("      <LineStyle>\r\n".getBytes());
            out.write("        <color>64eeee17</color>\r\n".getBytes());
            out.write("        <width>6</width>\r\n".getBytes());
            out.write("      </LineStyle>\r\n".getBytes());
            out.write("    </Style>\r\n".getBytes());
            recordStore = RecordStore.openRecordStore(trackName, false);
            int numRecords = recordStore.getNumRecords();
            byte[] bytes = new byte[6];
            for (int recordId = 1; recordId <= numRecords; recordId++) {
                recordStore.getRecord(recordId, bytes, 0);
                int latitude = 0x00ff0000 & (bytes[0] << 16);
                latitude |= 0x0000ff00 & (bytes[1] << 8);
                latitude |= 0x000000ff & bytes[2];
                latitude |= (latitude & 0x00800000) != 0 ? 0xff000000 : 0;
                int longitude = 0x00ff0000 & (bytes[3] << 16);
                longitude |= 0x0000ff00 & (bytes[4] << 8);
                longitude |= 0x000000ff & bytes[5];
                longitude |= (longitude & 0x00800000) != 0 ? 0xff000000 : 0;
                if (recordId == 1) {
                    out.write("    <Placemark>\r\n".getBytes());
                    out.write("      <name>Start</name>\r\n".getBytes());
                    out.write("      <styleUrl>#waypoint</styleUrl>\r\n".getBytes());
                    out.write("      <Point>\r\n".getBytes());
                    out.write("        <coordinates>\r\n".getBytes());
                    writeKmlPosition(out, latitude, longitude);
                    out.write("        </coordinates>\r\n".getBytes());
                    out.write("      </Point>\r\n".getBytes());
                    out.write("    </Placemark>\r\n".getBytes());
                    out.write("    <Placemark>\r\n".getBytes());
                    out.write("      <name>Track</name>\r\n".getBytes());
                    out.write("      <styleUrl>#track</styleUrl>\r\n".getBytes());
                    out.write("      <LineString>\r\n".getBytes());
                    out.write("        <coordinates>\r\n".getBytes());
                }
                writeKmlPosition(out, latitude, longitude);
                if (recordId == numRecords) {
                    out.write("        </coordinates>\r\n".getBytes());
                    out.write("      </LineString>\r\n".getBytes());
                    out.write("    </Placemark>\r\n".getBytes());
                    out.write("    <Placemark>\r\n".getBytes());
                    out.write("      <name>End</name>\r\n".getBytes());
                    out.write("      <styleUrl>#waypoint</styleUrl>\r\n".getBytes());
                    out.write("      <Point>\r\n".getBytes());
                    out.write("        <coordinates>\r\n".getBytes());
                    writeKmlPosition(out, latitude, longitude);
                    out.write("        </coordinates>\r\n".getBytes());
                    out.write("      </Point>\r\n".getBytes());
                    out.write("    </Placemark>\r\n".getBytes());
                }
                if (gauge != null)
                    gauge.setValue(recordId * 10 / numRecords);
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException ex) { }
            }
            out.write("  </Document>\r\n".getBytes());
            out.write("</kml>\r\n".getBytes());
        } finally {
            try {
                if (recordStore != null)
                    recordStore.closeRecordStore();
            } catch (Exception e) { }
        }
    }

    private static void writeKmlPosition(DataOutputStream out, int latitude, int longitude) throws IOException {
        out.write(new Float(longitude).Div(DEGREESTOINT).toString().getBytes());
        out.write(",".getBytes());
        out.write(new Float(latitude).Div(DEGREESTOINT).toString().getBytes());
        out.write(",0\r\n".getBytes());
    }
    
    private static void writeGpx(DataOutputStream out, String trackName, Gauge gauge) throws IOException, RecordStoreException {
        RecordStore recordStore = null;
        try { 
            out.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n".getBytes());
            out.write("<gpx version=\"1.0\"\r\n".getBytes());
            out.write("creator=\"GpsTrack - http://www.qcontinuum.org/gpstrack\"\r\n".getBytes());
            out.write("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"".getBytes());
            out.write(" xmlns=\"http://www.topografix.com/GPX/1/0\"\r\n".getBytes());
            out.write("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\r\n".getBytes());
            out.write("<trk>\r\n".getBytes());
            out.write("<name>".getBytes());
            out.write(trackName.getBytes());
            out.write("</name>\r\n".getBytes());
            out.write("<trkseg>\r\n".getBytes());
            recordStore = RecordStore.openRecordStore(trackName, false);
            int numRecords = recordStore.getNumRecords();
            byte[] bytes = new byte[6];
            for (int recordId = 1; recordId <= numRecords; recordId++) {
                recordStore.getRecord(recordId, bytes, 0);
                int latitude = 0x00ff0000 & (bytes[0] << 16);
                latitude |= 0x0000ff00 & (bytes[1] << 8);
                latitude |= 0x000000ff & bytes[2];
                latitude |= (latitude & 0x00800000) != 0 ? 0xff000000 : 0;
                int longitude = 0x00ff0000 & (bytes[3] << 16);
                longitude |= 0x0000ff00 & (bytes[4] << 8);
                longitude |= 0x000000ff & bytes[5];
                longitude |= (longitude & 0x00800000) != 0 ? 0xff000000 : 0;
                out.write("<trkpt lat=\"".getBytes());
                out.write(new Float(latitude).Div(DEGREESTOINT).toString().getBytes());
                out.write("\" lon=\"".getBytes());
                out.write(new Float(longitude).Div(DEGREESTOINT).toString().getBytes());
                out.write("\"></trkpt>\r\n".getBytes());
                if (gauge != null)
                    gauge.setValue(recordId * 10 / numRecords);
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException ex) { }
            }
            out.write("</trkseg>\r\n".getBytes());
            out.write("</trk>\r\n".getBytes());
            out.write("</gpx>\r\n".getBytes());
        } finally {
            try {
                if (recordStore != null)
                    recordStore.closeRecordStore();
            } catch (Exception e) { }
        }
    }
    Vector m_latTrack = new Vector();
    Vector m_lngTrack = new Vector();
    public void getGPSTrack()
    {
        m_latTrack.removeAllElements();
        m_lngTrack.removeAllElements();
        RecordStore recordStore = null;
        
        try
        {
            recordStore = RecordStore.openRecordStore(mTrackName, false);
            int numRecords = recordStore.getNumRecords();
            byte[] bytes = new byte[6];
            for (int recordId = 1; recordId <= numRecords; recordId++) {
                recordStore.getRecord(recordId, bytes, 0);
                int latitude = 0x00ff0000 & (bytes[0] << 16);
                latitude |= 0x0000ff00 & (bytes[1] << 8);
                latitude |= 0x000000ff & bytes[2];
                latitude |= (latitude & 0x00800000) != 0 ? 0xff000000 : 0;
                int longitude = 0x00ff0000 & (bytes[3] << 16);
                longitude |= 0x0000ff00 & (bytes[4] << 8);
                longitude |= 0x000000ff & bytes[5];
                longitude |= (longitude & 0x00800000) != 0 ? 0xff000000 : 0;
                m_latTrack.addElement(new Float(latitude).Div(DEGREESTOINT).toString());
                m_lngTrack.addElement(new Float(longitude).Div(DEGREESTOINT).toString());
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException ex) { }
            }
            System.out.println("Kich thuoc Track : "+m_latTrack.size());
            for(int i = 0 ;i < m_latTrack.size() ; i++)
            {
                String m_StrTmp = (String)m_latTrack.elementAt(i);
                String m_StrTmp1 = (String)m_lngTrack.elementAt(i);
                System.out.println("Toa do : "+m_StrTmp+"         "+m_StrTmp1);
            }
        }
        catch(Exception e)
        {
            
        }
    }

    private static void writeCsv(DataOutputStream out, String trackName, Gauge gauge) throws IOException, RecordStoreException {
        RecordStore recordStore = null;
        try { 
            out.write("latitude,longitude\r\n".getBytes());
            recordStore = RecordStore.openRecordStore(trackName, false);
            int numRecords = recordStore.getNumRecords();
            byte[] bytes = new byte[6];
            for (int recordId = 1; recordId <= numRecords; recordId++) {
                recordStore.getRecord(recordId, bytes, 0);
                int latitude = 0x00ff0000 & (bytes[0] << 16);
                latitude |= 0x0000ff00 & (bytes[1] << 8);
                latitude |= 0x000000ff & bytes[2];
                latitude |= (latitude & 0x00800000) != 0 ? 0xff000000 : 0;
                int longitude = 0x00ff0000 & (bytes[3] << 16);
                longitude |= 0x0000ff00 & (bytes[4] << 8);
                longitude |= 0x000000ff & bytes[5];
                longitude |= (longitude & 0x00800000) != 0 ? 0xff000000 : 0;
                out.write(new Float(latitude).Div(DEGREESTOINT).toString().getBytes());
                out.write(",".getBytes());
                out.write(new Float(longitude).Div(DEGREESTOINT).toString().getBytes());
                out.write("\r\n".getBytes());
                if (gauge != null)
                    gauge.setValue(recordId * 10 / numRecords);
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException ex) { }
            }
        } finally {
            try {
                if (recordStore != null)
                    recordStore.closeRecordStore();
            } catch (Exception e) { }
        }
    }

    private static void writeBinary(DataOutputStream out, String trackName, Gauge gauge) throws IOException, RecordStoreException {
        RecordStore recordStore = null;
        try { 
            recordStore = RecordStore.openRecordStore(trackName, false);
            int numRecords = recordStore.getNumRecords();
            byte[] bytes = new byte[6];
            for (int recordId = 1; recordId <= numRecords; recordId++) {
                recordStore.getRecord(recordId, bytes, 0);
                out.write(bytes);
                if (gauge != null)
                    gauge.setValue(recordId * 10 / numRecords);
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException ex) { }
            }
        } finally {
            try {
                if (recordStore != null)
                    recordStore.closeRecordStore();
            } catch (Exception e) { }
        }
    }

    public static void writeHttp(int format, String trackName, String userName, Gauge gauge) throws IOException, RecordStoreException {
        HttpConnection httpConnection = null;
        DataOutputStream dataOutputStream = null;
        Preferences preferences = MobileGPS.getPreferences();
        try {
            httpConnection =(HttpConnection)
                Connector.open("http://qcontinuum.org/scripts/gpstrack.cgi");
            httpConnection.setRequestMethod(HttpConnection.POST);
            httpConnection.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0");
            String boundary = "AaB03x";
            httpConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
            dataOutputStream = new DataOutputStream(httpConnection.openDataOutputStream());
            dataOutputStream.write(("--" + boundary + "\r\n").getBytes());
            writeHttpParam("platform", System.getProperty("microedition.platform"), dataOutputStream);
            dataOutputStream.write(("--" + boundary + "\r\n").getBytes());
            writeHttpParam("gps", preferences.getBluetoothHost(), dataOutputStream);
            dataOutputStream.write(("--" + boundary + "\r\n").getBytes());
            writeHttpParam("format", getFormatName(format), dataOutputStream);
            dataOutputStream.write(("--" + boundary + "\r\n").getBytes());
            writeHttpParam("email", userName, dataOutputStream);
            dataOutputStream.write(("--" + boundary + "\r\n").getBytes());
            writeHttpFile("file", trackName, dataOutputStream, gauge);
            dataOutputStream.write(("--" + boundary + "--\r\n").getBytes());
        } finally {
            try {
                if (dataOutputStream != null)
                    dataOutputStream.close();
                if (httpConnection != null)
                    httpConnection.close();
            } catch (Exception e) { }
        }
    }

    private static void writeHttpParam(String name, String value, DataOutputStream out) {
        try {
            out.write(("Content-Disposition: form-data; name=\"" + name + "\"\r\n\r\n").getBytes());
            if (value != null)
                out.write(value.getBytes());
            out.write(("\r\n").getBytes());
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    private static void writeHttpFile(String name, String trackName, DataOutputStream out, Gauge gauge)
    {
        try {
            out.write(("Content-Disposition: form-data; name=\"" + name + "\"; filename=\""
            + trackName + "\"\r\n").getBytes());
            out.write(("Content-Type: application/octet-stream" + "\r\n\r\n").getBytes());
            Track.write(FORMAT_BINARY, out, trackName, gauge);
            out.write(("\r\n").getBytes());
        } catch (Exception e) { 
            System.out.println(e.toString()); 
        }
    }

}
